use cmsis_rtos2_rs::static_mem::{MsgQueue, Task, Thread};
use proto_bytes::{
    frame::{Proto, Res2}, message::Message, package::ty, ProtoEncode2
};
use utils::log;
use crate::conn::write_wait_ack;

struct Context {
    thread: Thread<2048>,
    mq: MsgQueue<Message, 5>,
}

impl Context {
    const fn uninit() -> Self {
        Self {
            thread: Thread::uninit(),
            mq: MsgQueue::uninit(),
        }
    }

    unsafe fn init_once(&self) {
        self.mq.init_once();
        self.thread.spawn(&HandlerTask);
    }
}

static CTX: Context = Context::uninit();
struct HandlerTask;

impl Task for HandlerTask {
    fn run(&self) {
        log!("handler task start");
        loop {
            if let Some(msg) = CTX.mq.poll(100) {
                on_dispatch(msg);
            } else {
                on_idle();
            }
        }
    }
}

#[derive(Clone, Copy)]
pub enum Response {
    Empty(()),

    #[cfg(feature = "cp_cargo")]
    Raw(&'static [u8]),

    #[cfg(feature = "cp_heat")]
    Ice(crate::cp_heat::handler::IceData),

    #[cfg(feature = "cp_heat")]
    U16(u16),
}

impl ProtoEncode2 for Response {
    unsafe fn encode(self, _ptr: *mut u8) -> usize {
        match self {
            Self::Empty(()) => 0,

            #[cfg(feature = "cp_heat")]
            Self::U16(v) => {
                v.encode(_ptr);
                2
            }

            #[cfg(feature = "cp_cargo")]
            Self::Raw(buf) => buf.encode(_ptr),

            #[cfg(feature = "cp_heat")]
            Self::Ice(data) => {
                data.cmd2.encode(_ptr);
                data.data1.encode(_ptr.add(1));
                data.data2.encode(_ptr.add(2));
                3
            }
        }
    }
}

fn on_idle() {
    #[cfg(feature = "cp_cargo")]
    crate::cp_cargo::handler::on_idle();

    #[cfg(feature = "cp_door")]
    crate::cp_door::handler::on_idle();
}   


fn on_dispatch(msg: Message) {

    let msg_ref = msg.as_ref();

    let req = msg_ref.into_req();
    
    #[cfg(feature = "cp_cargo")]
    let ret = crate::cp_cargo::handler::on_request(req);

    #[cfg(feature = "cp_drop")]
    let ret = crate::cp_drop::handler::on_request(req);

    #[cfg(feature = "cp_door")]
    let ret = crate::cp_door::handler::on_request(req);

    #[cfg(feature = "cp_heat")]
    let ret = {
        use crate::cp_heat::handler::on_request;
        on_request(req)
    };
    
    #[cfg(feature = "cp_weight")]
    let ret = crate::cp_weight::handler::on_request(req);

    write_wait_ack(Proto {
        ty: ty::RES,
        seq: req.seq(),
        src: req.dst(),
        dst: req.src(),
        body: Res2 {
            cmd: req.cmd(),
            ret,
        }
    });
}

pub unsafe fn start() {
    CTX.init_once();
}

pub fn post(msg: Message) {
    _ = CTX.mq.post(msg);
}
